/*******************************************************************************
* @file    
* @brief   虚拟串口
*******************************************************************************/
#include "bsp.h" 
#include "pincfg.h"
#include "os_obj.h"  
#include "dataproc.h" 
#include "com_obj.h" 
#include "cpu_it.h"
#include "str_ext.h"

/*******************************************************************************
* @brief   基本接口
*******************************************************************************/
#define com_dir_rx_enable()
#define com_dir_tx_enable()

static void com_hal_init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity);
static void com_rx_enbale(uint8_t xEnable);
static void com_tx_enbale(uint8_t xEnable);
static void com_timer_handle(void);
static void com_fun_init(uint8_t comMode);

/*******************************************************************************
* @brief   本地变量
*******************************************************************************/
static uint8_t b_dir_ctr_req;
static const com_fun_type com_fun;

/*******************************************************************************
* @brief   虚拟接口
*******************************************************************************/
#ifndef ComObj

/*******************************************************************************
* @brief   接口功能
*******************************************************************************/
#define COM_RX_EN                   1
#define COM_RX_CALL_BACK_EN         0
#define MB_RS_485_MODE_EN           0
#define MB_RS_485_HALDIR_EN         0

#define comPORT						UART0
#define COM_IRQn					UART0_IRQn
#define comIsr						COM_UARTsim_IRQHandler
#define comRST						UART0_RST
#define ComObj						com.obj00

static uint8_t com_tx_buff[2];
static uint8_t com_rx_buff[2];

/*******************************************************************************
* @brief   FIFO 尺寸
*******************************************************************************/
#define COM_PORT_FIFO_SIZE      14

/*******************************************************************************
* @brief   PIN CFG.
*
* @ Pass/ Fail criteria: none
*******************************************************************************/
static void com_pin_cfg(void)
{
}

/*******************************************************************************
* @brief   	com port init .
*
* @ Pass/ Fail criteria: none
*******************************************************************************/
void com_sim_Init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity)
{
	com_hal_init(comMode, ulBaudRate, eParity);
}

#endif

/*******************************************************************************
* @brief   串口底层操作
*******************************************************************************/
#define UART_SEND_DAT(dat)  comPORT->DAT = dat
#define UART_RCV_DAT()      (comPORT->DAT)
#define UART_RX_NE_ST()     ((comPORT->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk) == 0) //rx not empty
#define UART_TX_NF_ST()     (0 == (comPORT->FIFOSTS & UART_FIFOSTS_TXFULL_Msk))  //tx not full
#define UART_RX_INT_EN()    comPORT->INTEN |= (UART_INTEN_RDAIEN_Msk | UART_INTEN_TOCNTEN_Msk | UART_INTEN_RXTOIEN_Msk) //开启RX超时功能，开启超时中断，接收阀值中断
#define UART_RX_INT_DIS()   comPORT->INTEN &= ~(UART_INTEN_RDAIEN_Msk | UART_INTEN_TOCNTEN_Msk | UART_INTEN_RXTOIEN_Msk)
#define UART_RX_ERR_ST()    (comPORT->FIFOSTS & (UART_FIFOSTS_PEF_Msk | UART_FIFOSTS_RXOVIF_Msk))
#define UART_RX_ERR_CLR()   comPORT->FIFOSTS = UART_FIFOSTS_PEF_Msk | UART_FIFOSTS_RXOVIF_Msk;
#define UART_TX_INT_EN()    comPORT->INTEN |= UART_INTEN_THREIEN_Msk
#define UART_TX_INT_DIS()   comPORT->INTEN &= ~UART_INTEN_THREIEN_Msk
#define UART_TX_TC_ST()     (comPORT->INTSTS & UART_INTSTS_TXENDIF_Msk)

// 中断状态获取
#define UART_INT_ST_GET()	IntSt = comPORT->INTSTS
#define UART_INT_RDA		(IntSt & UART_INTSTS_RDAINT_Msk)
#define UART_INT_RXTO		(IntSt & UART_INTSTS_RXTOINT_Msk)
#define UART_INT_TXE		(IntSt & UART_INTSTS_THREINT_Msk)

// 清空接收
__STATIC_FORCEINLINE void UART_RX_DAT_CLR()       
{
    volatile uint8_t u8Temp;
    while(!(comPORT->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk))
    {
        u8Temp = comPORT->DAT;
    }
}

// 接收错误检测
__STATIC_FORCEINLINE void COM_HAL_ERR_CHECK() 
{                              
        if (UART_RX_ERR_ST())               
		{                            
            ComObj.St.bits.err_hal = 1;      
            UART_RX_ERR_CLR(); 
		}                         
}

#include "com_fun_ptr.h"

/*****************************************************************************
* @brief   com object init.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_fun_init(uint8_t comMode)
{
    ComObj.St.bits.run_en = 1;
    ComObj.master_wait_time_sv = 200;
    ComObj.rcv_end_keep_sv = 3;
    ComObj.rx_buff = com_rx_buff;
    ComObj.tx_buff = com_tx_buff;
    ComObj.p_tx = com_tx_buff;
    ComObj.rx_buff_size = sizeof(com_rx_buff);
    ComObj.tx_buff_size = sizeof(com_tx_buff);
    
    ComObj.fun = &com_fun;
    
    ComObj.b_master_mode = comMode;
    
    app_timer_task_add(com_timer_handle);
}

/*****************************************************************************
* @brief   	com modle init .
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_hal_init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity)
{
    uint32_t us10TimerSv;
    uint32_t ClocksPer2ms;
    uint32_t parity_set;
	
    com_pin_cfg();
    
    set_u8(&ComObj, 0, sizeof(ComObj));
    
	// 奇偶校验
    if (COM_PAR_NONE_1S == eParity)
    {
        parity_set = UART_PARITY_NONE;
    }
    else if (COM_PAR_ODD == eParity)
    {
        parity_set = UART_PARITY_ODD;
    }
    else
    {
        parity_set = UART_PARITY_EVEN;
    }
	
	// 打开串口
    UART_Open(comPORT, ulBaudRate);
    UART_SetLineConfig(comPORT, ulBaudRate, UART_WORD_LEN_8, parity_set, UART_STOP_BIT_1);

    // 接收FIFO触发深度
    comPORT->FIFO &= ~UART_FIFO_RFITL_Msk;
    comPORT->FIFO |= UART_FIFO_RFITL_14BYTES;
    comPORT->FIFO &= ~UART_FIFO_RXOFF_Msk;
    
	
    // 设置超时时间 t单位us
	// bits * (1000000 / baud) = t
	// bits = t * baud / 1000000
    if (ulBaudRate > 19200)
    {
        uint32_t clk_num;
        
        clk_num = 2000 * ulBaudRate / 1000000;
        
		if(clk_num < 41)
			clk_num = 41;
		
        comPORT->TOUT = (1UL<<8) |(clk_num);
    }
    else
    {
        // Time-out Register ,TX-DLY=0, TX-TIMEOUT=40
		comPORT->TOUT = (1UL<<8) |(41);
    }
    
	// 软件超时时间
    us10TimerSv = com_timer_cal(ulBaudRate, 12*(5+16), 300, 20000);
    ComObj.rcv_end_time_sv = us10TimerSv/100 + 3;
    
    if (comMode > 0)
    {
        ComObj.rcv_end_time_sv ++;
    }
	
	nvic_irq_set(COM_IRQn, 0x0A, 1);
    
    com_fun_init(comMode);
    
    com_rx_enbale(ENABLE);
}

/*****************************************************************************
* @brief   com end timer.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_dir_handle()
{
    if (b_dir_ctr_req)
    {    
#if ((MB_RS_485_MODE_EN > 0) && (MB_RS_485_HALDIR_EN == 0))
		// RS485模式且软件控制方向时，发送结束切换为接收模式
        if ( UART_TX_TC_ST())
        {
            b_dir_ctr_req = 0;
            ComObj.snd_st = COM_TX_IDLE;        //发送结束
            com_tx_enbale(DISABLE);
            if (ComObj.b_master_mode)           //主机进入接收等待状态
            {
                ComObj.rcv_st = COM_RX_WAIT;
                com_rx_enbale(ENABLE);
            }
        }
#endif 
    }         
}

/*****************************************************************************
* @brief   com end timer.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_timer_handle(void)
{
    com_dir_handle();
    
    if(ComObj.St.bits.cycle_mode)
    {
		// 循环模式检测启动
        if (ComObj.index_tx_send != ComObj.index_tx_fill)
        {
            com_tx_enbale(1);
        }
    }

	com_timer_run(&ComObj);
}

/*****************************************************************************
* @brief   循环模式中断处理
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_isr_cycle_mode(void) 
{
    volatile uint32_t IntSt;
     
    UART_INT_ST_GET();
    ComObj.master_wait_time_cnt = 0;
    
    #if COM_RX_EN > 0
    if(UART_INT_RDA)
    {
        uint16_t ReadCnt;
        
        // FIFO 中断
        ComObj.St.bits.port_st = 1;

        // 接收状态
        ComObj.rcv_st = COM_RX_RCV; //指示正在接收数据
        ComObj.b_timer_on = 1;
        ComObj.rcv_end_time_cnt = 0;
		
        // 读数据
		#if COM_PORT_FIFO_SIZE > 1
        ReadCnt = COM_PORT_FIFO_SIZE-1; //FIFO read
		#else
		ReadCnt = 1; //FIFO read
		#endif
		
        while (ReadCnt--)
        {
            uint8_t datr;
            datr = UART_RCV_DAT();
            ComObj.rx_buff[ComObj.index_rx_rcv++] = datr; //指针越界检查
            
            if(ComObj.index_rx_rcv >= ComObj.rx_buff_size)
                ComObj.index_rx_rcv = 0;
            
            #if COM_RX_CALL_BACK_EN > 0
            if(ComObj.rx_call_back)
            {
                ComObj.rx_call_back(datr);
            }
            #endif
        }
    }
    else if(UART_INT_RXTO)
    {
        //超时中断
        ComObj.St.bits.port_st = 1;

        //读数据
        ComObj.rcv_st = COM_RX_RCV; //指示正在接收数据
        ComObj.b_timer_on = 1;
        ComObj.rcv_end_time_cnt = 0;
        
        while (UART_RX_NE_ST())
        {
            uint8_t datr;
            datr = UART_RCV_DAT();
            ComObj.rx_buff[ComObj.index_rx_rcv++] = datr; //指针越界检查
            
            if(ComObj.index_rx_rcv >= ComObj.rx_buff_size)
                ComObj.index_rx_rcv = 0;
            
            #if COM_RX_CALL_BACK_EN > 0
            if(ComObj.rx_call_back)
            {
                ComObj.rx_call_back(datr);
            }
            #endif
        }
    }
    else if(UART_INT_TXE)
    #else
    if(UART_INT_TXE)
    #endif
    //发送缓冲空触发
    {
        if (ComObj.index_tx_send == ComObj.index_tx_fill)
        {
            //发送已结束 //关闭中断
            com_tx_enbale(DISABLE);
        }
        else
        {
            //发送空中断
            while (UART_TX_NF_ST()) //缓冲未满
            {
                if (ComObj.index_tx_send != ComObj.index_tx_fill) //写缓冲区结束
                {
                    //继续发送
                    UART_SEND_DAT(ComObj.tx_buff[ComObj.index_tx_send++]);
                    
                    if(ComObj.index_tx_send >= ComObj.tx_buff_size)
                    {
                        ComObj.index_tx_send = 0;
                    }
					__DSB();
                }
                else
                {
                    //发送结束
                    break;
                }
            }
        }
    }
    else
    {
        //未知错误，应重新初始化串口设备
		//ComObj.St.bits.err_hal = 1;
		//nvic_irq_set(COM_IRQn,0x0F,0);
    }
}

/*****************************************************************************
* @brief   主从模式中断处理
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_isr_signal_mode(void) 
{
    uint32_t IntSt;
    uint16_t ReadCnt;
    volatile uint8_t Data;

    ComObj.master_wait_time_cnt = 0;

    UART_INT_ST_GET();

    #if COM_RX_EN > 0
    if (UART_INT_RDA)
    {
        //FIFO 中断
        ComObj.St.bits.port_st = 1;

        com_timer_en(&ComObj);

        //状态非法，禁止接收
        if (COM_RX_RCVEND == ComObj.rcv_st)
        {
            while (UART_RX_NE_ST())
            {
                Data = UART_RCV_DAT();
            }
            UART_RX_ERR_CLR();
            return;
        }

        //读数据
        ComObj.rcv_st = COM_RX_RCV;			//指示正在接收数据
        #if COM_PORT_FIFO_SIZE >= 2
        ReadCnt = COM_PORT_FIFO_SIZE - 1;	//缓冲区至少留一字节数据，否则若无数据继续接收，超时中断将不会出发
        #else
        ReadCnt = 1;
        #endif
        while (ReadCnt--)
        {
            if (ComObj.index_rx_rcv < ComObj.rx_buff_size)
            {
                uint8_t datr;
                datr = UART_RCV_DAT();
                ComObj.rx_buff[ComObj.index_rx_rcv++] = datr; //指针越界检查
                
                #if COM_RX_CALL_BACK_EN > 0
                if(ComObj.rx_call_back)
                {
                    ComObj.rx_call_back(datr);
                }
                #endif
            }
            else
            {
                Data = UART_RCV_DAT();
            }
        }
        COM_HAL_ERR_CHECK();
    }
	else if (UART_INT_RXTO)
    {
        //超时中断
        ComObj.St.bits.port_st = 1;

        //com_timer_en(&ComObj);

        //状态非法，禁止接收
        if (COM_RX_RCVEND == ComObj.rcv_st)
        {
            while (UART_RX_NE_ST())
            {
                Data = UART_RCV_DAT();
            }
            UART_RX_ERR_CLR();
            return;
        }

        //读数据
        ComObj.rcv_st = COM_RX_RCV; //指示正在接收数据
        while (UART_RX_NE_ST())
        {
            if (ComObj.index_rx_rcv < ComObj.rx_buff_size)
            {
                uint8_t datr;
                
                datr = UART_RCV_DAT();
                ComObj.rx_buff[ComObj.index_rx_rcv++] = datr; //指针越界检查
                
                #if COM_RX_CALL_BACK_EN > 0
                if(ComObj.rx_call_back)
                {
                    ComObj.rx_call_back(datr);
                }
                #endif
            }
            else
            {
                Data = UART_RCV_DAT();
            }
        }
        COM_HAL_ERR_CHECK();
        //发送消息
        ComObj.rcv_st = COM_RX_RCVEND;
    }
    else if (UART_INT_TXE)
    #else
    if (UART_INT_TXE)
    #endif
    {
        if (ComObj.index_tx_send >= ComObj.index_tx_fill)
        {
            //发送已结束 //关闭中断
            UART_TX_INT_DIS();
#if ((MB_RS_485_MODE_EN > 0) && (MB_RS_485_HALDIR_EN == 0))
            //延时结束，此方法针对RS485接口，对于全双工通信可直接结束
            b_dir_ctr_req = 1;
#else
            ComObj.snd_st = COM_TX_IDLE;        //发送结束
            com_tx_enbale(DISABLE);
            if (ComObj.b_master_mode) //主机进入接收等待状态
            {
                ComObj.rcv_st = COM_RX_WAIT;
                com_rx_enbale(ENABLE);
            }
#endif
        }
        else
        {
            if(ComObj.dat_mode == COM_DAT_DATA)
            {
                //发送空中断 //数据模式
                while (UART_TX_NF_ST())
                {
                    if (ComObj.index_tx_send < ComObj.index_tx_fill) //写缓冲区结束
                    {
                        //继续发送
                        UART_SEND_DAT(ComObj.p_tx[ComObj.index_tx_send++]);
                    }
                    else
                    {
                        //发送结束
                        break;
                    }
                }
            }
            else
            {
                //字符串模式
                while (UART_TX_NF_ST())
                {
                    uint8_t dat_now;
                    dat_now = ComObj.p_tx[ComObj.index_tx_send++];
                    
                    if(0 != dat_now)
                    {
                        UART_SEND_DAT(dat_now);
                    }
                    else
                    {
                        ComObj.index_tx_fill = ComObj.index_tx_send;
                        //发送结束
                        break;
                    }
                }
            }
        }
    }
    else
    {
        //未知错误，应重新初始化串口设备
		//ComObj.St.bits.err_hal = 1;
		//nvic_irq_set(COM_IRQn,0x0F,0);
    }
}

/*****************************************************************************
* @brief   中断入口
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void comIsr(void) 
{
    if(ComObj.St.bits.cycle_mode)
    {
        com_isr_cycle_mode();
    }
    else
    {
        com_isr_signal_mode();
    }
}

/*****************************************************************************
* @brief   Uart Rx En or Dis.
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_rx_enbale(uint8_t xRxEnable)
{
    volatile uint8_t u8Temp;

    if (xRxEnable)
    {
        #if COM_RX_EN > 0
        //清空接收缓冲
        UART_RX_DAT_CLR();
        
        //清除帧错误和奇偶校验错误
        UART_RX_ERR_CLR();
        
        //开始接收
        UART_RX_INT_EN();
        com_dir_rx_enable();
        #else
        UART_RX_INT_DIS();
        #endif
    }
    else
    {
        UART_RX_INT_DIS();
    }
}

/*****************************************************************************
* @brief   Uart Tx En or Dis.
* @ Pass/ Fail criteria: none
*****************************************************************************/
static void com_tx_enbale(uint8_t xEnable)
{
    if (xEnable)
    {
         //准备发送
        com_dir_tx_enable();
        
        ComObj.snd_st = COM_TX_XMIT;
        //Interrupt Enable Register 打开发送空中断
        UART_TX_INT_EN();
    }
    else
    {
        UART_TX_INT_DIS();
        com_dir_rx_enable();
        ComObj.snd_st = COM_TX_IDLE;
    }
}


